﻿using Common.Net;
using Common.Net.Data;
using Common.Net.Extensions;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.Json.Nodes;
using System.Reflection;
using DBUtil.Net.Extensions;

namespace DBUtil.Net.Builders
{
    public class NodeCallBack(Func<Expression, bool> CanCallBack, Func<Expression, Func<string>, Func<Expression, string>, string> Visit, object Flag = null)
    {
        public object Flag { get; set; } = Flag;
        public Func<Expression, bool> CanCallBack { set; get; } = CanCallBack;
        /// <summary>
        /// (exp,()=>string,(exp)=>string)=>string
        /// 参数1: exp, 表示将要解析的表达式
        /// 参数2: 委托, 称为: next 表示让步后面的执行
        /// 参数3: 委托, 称为: visit 表示开启一个新的访问
        /// </summary>
        public Func<Expression, Func<string>, Func<Expression, string>, string> Visit { set; get; } = Visit;
    }
    public class MethodHookContext
    {
        public DBAccess db { get; set; }
        public Expression Expression { get; set; }
        public (string Name, List<(string name, bool isGeneric, Type type)> GenericTypes) Reflect { get; set; }
        /// <summary>
        /// 提前判断表达式内是否将枚举处理成字符串
        /// </summary>
        public Func<Expression, (bool isEnumString, Type enumType)> IsEnumString { get; set; }
        /// <summary>
        /// 尝试获取表达式的常量值
        /// </summary>
        public Func<Expression, Dictionary<ParameterExpression, object>, string, Result> GetConstant { get; set; }
        public Dictionary<ParameterExpression, object> MidValues { get; set; }
        public Func<Expression, string> Visit { get; set; }
        /// <summary>
        /// 仅在必要的情况下, 将一对小括号加到两边
        /// </summary>
        public Func<string, string> WrapBracket { get; set; }
        /// <summary>
        /// 获取表达式的jsonseg,如果是常量的话,要考虑目标位置接受的json类型
        /// </summary>
        public Func<Expression[], EnumJsonAcceptAsType[], string[]> GetJsonSqlSeg { get; set; }
        /// <summary>
        /// 监控并记录生成的json_table情况
        /// </summary>
        public Func<LambdaExpression, (List<(string memberName, string colPath, string colType)>, string)> JsonTableSpread { get; set; }
        /// <summary>
        /// 将记录的json_table展开情况生成columns信息
        /// </summary>
        public Func<List<(string memberName, string colPath, string colType)>, string> JsonTableParaToColumns { get; internal set; }
        public Action<KeyValuePair<ParameterExpression, string>, bool> AddParameter { get; set; }
        public Action<ParameterExpression> RemoveParameter { get; set; }
    }
    public class MemberHookContext
    {
        public DBAccess db { get; set; }
        public Expression Expression { get; set; }
        public string ParameterString { get; set; }
        public string TypeFullName { get; set; }
        public Func<string, string> WrapBracket { get; set; }
        public Func<Expression, bool> IsJsonParameter { get; set; }
        public Func<string> GetDefault { get; set; }
    }

    public static class BuilderHelper
    {
        /// <summary>
        /// setIgnore 过滤
        /// </summary>
        internal static void ApplySetIgnore(List<SetListItem> setList, List<SetIgnoreItem> setIgnores, List<EntityPropertyInfo> entityProperties)
        {
            //进行 setIgnore 过滤
            for (int i = 0; i < setIgnores.Count; i++)
            {
                var setIgnoreItem = setIgnores[i];
                if (setIgnoreItem.Type == EnumSetIgnoreType.SetColumn)
                {
                    var col = setIgnoreItem.ColumnNames[0];
                    var colNoQuote = setIgnoreItem.ColumnNamesNoQuote[0];
                    if (setIgnoreItem.JsonDepthProps.IsNotNullOrEmpty())
                    {
                        //SetColumn(i=>i.Ext.Detail.Name,"jack")
                        var prop = entityProperties.FirstOrDefault(i => i.ColumnNamePure == colNoQuote);
                        setList.Add(new SetListItem(prop, col, colNoQuote, setIgnoreItem.Value, true, setIgnoreItem.JsonDepthProps));
                    }
                    else
                    {
                        setList.Remove(i => i.ColumnNameNoQuote == colNoQuote);
                        var prop = entityProperties.FirstOrDefault(i => i.ColumnNamePure == colNoQuote);
                        setList.Add(new SetListItem(prop, col, colNoQuote, setIgnoreItem.Value, true));
                    }
                }
                else if (setIgnoreItem.Type == EnumSetIgnoreType.IgnoreColumn)
                {
                    setIgnoreItem.ColumnNamesNoQuote.ForEach(col => setList.Remove(i => i.ColumnNameNoQuote == col));
                }
                else if (setIgnoreItem.Type == EnumSetIgnoreType.OnlyColumn)
                {
                    for (int j = setList.Count - 1; j >= 0; j--)
                    {
                        if (!setIgnoreItem.ColumnNamesNoQuote.Contains(setList[j].ColumnNameNoQuote))
                            setList.RemoveAt(j);
                    }
                }
            }
        }

        /// <summary>
        /// 把JsonStore列提取出来
        /// </summary>
        internal static List<SetListItem> SplitJsonStore(List<SetListItem> setList)
        {
            var jsonSetList = new List<SetListItem>();
            for (int i = 0; i < setList.Count; i++)
            {
                if (setList[i].PropertyInfo?.JsonBucket.IsNotNullOrEmptyOrWhiteSpace() == true)
                {
                    jsonSetList.Add(setList[i]);
                    setList.RemoveAt(i);
                    i--;
                }
            }
            return jsonSetList;
        }

        private static readonly Dictionary<ExpressionType, string> _cache = new()
        {
            {ExpressionType.Add,"+" },
            {ExpressionType.Subtract,"-" },
            {ExpressionType.Multiply,"*" },
            {ExpressionType.Divide,"/" },

            {ExpressionType.Equal,"=" },
            {ExpressionType.NotEqual,"<>" },
            {ExpressionType.GreaterThan,">" },
            {ExpressionType.GreaterThanOrEqual,">=" },
            {ExpressionType.LessThan,"<" },
            {ExpressionType.LessThanOrEqual,"<=" },

            {ExpressionType.AndAlso,"and" },
            {ExpressionType.OrElse,"or" },
            {ExpressionType.And,"and" },
            {ExpressionType.Or,"or" },
        };

        /// <summary>
        /// 将执行的 expression 解析为 Sql,支持 多参数,支持 select/insert/update中的表达式
        /// </summary>
        /// <param name="expression">要解析的表达式</param>
        /// <param name="db"></param>
        /// <param name="aliasesMap">已经预设的别名,如果想禁止别名,那么就设为null</param>
        /// <param name="parameters">参数,当 expression 不是lambda时,使用此参数方便定位</param>
        /// <param name="fixDic"></param>
        /// <param name="midValues"></param>
        /// <param name="enum2StringMap"></param>
        /// <param name="nodeCallBacks">一些回调函数,针对特定的节点类型</param>
        /// <returns></returns>
        internal static string ParseSql(Expression expression, DBAccess db
            , IEnumerable<KeyValuePair<ParameterExpression, string>> aliasesMap = null
            , IEnumerable<ParameterExpression> parameters = null
            , Dictionary<string, string> fixDic = null
            , Dictionary<ParameterExpression, object> midValues = null
            , Dictionary<ParameterExpression, bool> enum2StringMap = null
            , List<NodeCallBack> nodeCallBacks = null)
        {
            if (expression is LambdaExpression lambda)
            {
                parameters = lambda.Parameters;
                expression = lambda.Body;
            }
            var _parameters = new List<ParameterExpression>();
            if (parameters is not null) foreach (var parameter in parameters) _parameters.Add(parameter);

            var _aliases = new List<KeyValuePair<ParameterExpression, string>>();
            if (aliasesMap is not null) foreach (var alias in aliasesMap) _aliases.Add(alias);

            var _parameterJsons = new List<ParameterExpression>();

            //枚举转字符串
            var isEnumString = false;
            Type enumType = null;

            //嵌套的路径
            var nestMemberPath = new List<string>(8);
            var getNestMember = () => nestMemberPath.ToStringSeparated(".");

            return Visit(expression);
            string Visit(Expression exp)
            {
                #region nodeCallBacks 拦截
                /*
                 * 在下面A处有两个callback, c1里面的next会调用到c2,c2的next会调用到原来的逻辑
                 *                []
                 *               /   \
                 *             /       \
                 *  c1       [A]         []
                 *  c2      /   \
                 *         /     \
                 *        /       \
                 *      []         []
                 * 
                 */
                var callBacks = nodeCallBacks?.Where(i => i.CanCallBack(exp)).ToList();
                if (callBacks?.Count > 0)
                {
                    var ret = string.Empty;
                    int cur = -1;
                    string Next()
                    {
                        cur++;
                        if (cur < callBacks.Count)
                        {
                            var callBack = callBacks[cur];
                            ret = callBack.Visit(exp, Next, (newExp) => Visit(newExp));
                        }
                        else
                        {
                            ret = _visit(exp);
                        }
                        return ret;
                    }
                    return Next();
                }
                #endregion
                return _visit(exp);
                string _visit(Expression exp)
                {
                    switch (exp.NodeType)
                    {
                        case ExpressionType.Constant:
                            var val = (exp as ConstantExpression).Value;
                            if (isEnumString) return $"'{val.To(enumType)}'";
                            return db.ConvertToSqlSeg(val).Data;
                        case ExpressionType.Parameter:
                            {
                                var parameterExpression = exp as ParameterExpression;
                                if (midValues?.TryGetValue(parameterExpression, out var value) == true)
                                {
                                    //简化求值后的常量
                                    //if (isEnumString) return $"'{value.To(enumType)}'";
                                    if (isEnumString) return db.ConvertToSqlSeg(value, true).Data;
                                    return db.ConvertToSqlSeg(value).Data;
                                }

                                var reflect = parameterExpression.Type.GetClassFullName();
                                if (_aliases.IsNotNullOrEmpty())
                                {
                                    var tmp = _aliases.FirstOrDefault(i => i.Key == parameterExpression);
                                    //使用预设的别名
                                    if (tmp.Key != null) return tmp.Value;
                                }
                                //考虑分组
                                if (reflect.StartsWith("DBUtil.Builders.IGroupFilter")) return "DBUtil.Builders.IGroupFilter";
                                //默认别名规则
                                if (parameterExpression.Name != "_") return parameterExpression.Name;
                                return string.Empty;
                            }
                        case ExpressionType.MemberAccess:
                            {
                                var member = exp as MemberExpression;
                                var tmp = Visit(member.Expression);
                                if (member.Member.Name == "Value" && member.Expression.Type.IsNullable())
                                {
                                    //忽略 i.age_null.Value 中的 .Value
                                    return tmp;
                                }
                                var type = member.Expression.Type;
                                if (type.IsAssignableTo(typeof(System.Collections.IEnumerable)) && type != typeof(string) && member.Member.Name == "Count")
                                {
                                    var hook = db.GetHook("other:getArrayLength");
                                    var declareType = member.Expression.Type;
                                    if (hook != null) return hook(new object[] { tmp, declareType }) as string;
                                    else throw new Exception("缺少hook:other:getArrayLength!");
                                }
                                var typeFullName = type.GetClassFullName();
                                if (typeFullName.StartsWith("DBUtil.Builders.IGroupFilter"))
                                {
                                    //Select<Dto>(IGroupyFilter<T,TGroupKey> i=>i.Key)
                                    if (member.Member.Name == "Key")
                                    {
                                        if (fixDic.ContainsKey("DBUtil.Builders.IGroupFilter.Key")) return fixDic["DBUtil.Builders.IGroupFilter.Key"];
                                        return "DBUtil.Builders.IGroupFilter.Key";
                                    }
                                    //Select<Dto>(IGroupyFilter<T,TGroupKey> i=>i.Length)
                                    if (member.Member.Name == "Length") return "count(1)";
                                    throw new Exception($"无法解析表达式: {member}");
                                }
                                if (tmp?.StartsWith("DBUtil.Builders.IGroupFilter.Key") == true)
                                {
                                    //Select<Dto>(IGroupyFilter<T,TGroupKey> i=>i.Key.Age)
                                    return fixDic["DBUtil.Builders.IGroupFilter.Key." + member.Member.Name];
                                }
                                var getDefault = () =>
                                {
                                    var entityInfo = db.GetEntityInfoInternal(type);
                                    var colName = entityInfo.EntityPropertyInfos.FirstOrDefault(i => i.PropNamePure == member.Member.Name).SelectValueFunc(tmp);
                                    return colName;
                                };
                                //转移到hook操作
                                var memberHook = db.GetPrefixMatchHook($"[member]:{typeFullName}:{member.Member.Name}");
                                if (memberHook != null) return memberHook(new MemberHookContext
                                {
                                    Expression = member,
                                    ParameterString = tmp,
                                    TypeFullName = typeFullName,
                                    WrapBracket = Extensions.StringExtensions.WrapBracket,
                                    db = db,
                                    IsJsonParameter = i => i is ParameterExpression para && _parameterJsons.Contains(i),
                                    GetDefault = getDefault,
                                }).ToString();
                                return getDefault();
                            }
                        case ExpressionType.ListInit:
                            {
                                throw new Exception("无法处理: ExpressionType.ListInit,必须指定: nodeCallBack!");
                            }
                        case ExpressionType.NewArrayInit:
                            {
                                throw new Exception("无法处理: ExpressionType.NewArrayInit,必须指定: nodeCallBack!");
                            }
                        case ExpressionType.NewArrayBounds:
                            {
                                throw new Exception("无法处理: ExpressionType.NewArrayBounds,必须指定: nodeCallBack!");
                            }
                        //MemberInit 和 New 类似, 前者是 new Person{Id=1} 后者是 new{Id=1}
                        case ExpressionType.MemberInit:
                            {
                                //Select(i=>new Dto{ Id = i.Id, Name = i.Name })
                                var initExp = exp as MemberInitExpression;
                                var type = initExp.Type;
                                var entityInfo = db.GetEntityInfoInternal(type);

                                var bindings = initExp.Bindings;
                                var list = new List<string>();
                                for (int i = 0; i < bindings.Count; i++)
                                {
                                    var binding = bindings[i];
                                    //expr中的这个属性可能是常量
                                    var assign = binding as MemberAssignment;
                                    var exp2 = assign.Expression;
                                    string _val;
                                    var propName = binding.Member.Name;
                                    nestMemberPath.Add(propName);
                                    if (exp2.NodeType == ExpressionType.New || exp2.NodeType == ExpressionType.MemberInit)
                                    {
                                        //嵌套 Select(i => new { Ext = new { Ext2 = new { Id = i.Id } } }
                                        _val = Visit(exp2);
                                        list.Add(_val);
                                        nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                        continue;
                                    }
                                    if (exp2.NodeType == ExpressionType.Parameter && !DBAccess.IsSimple(exp2.Type))
                                    {
                                        //Select((i,j)=>new Info{Person = i}
                                        var entityInfo2 = db.GetEntityInfoInternal(exp2.Type);
                                        var prefix = Visit(exp2);
                                        var _memberName = getNestMember();
                                        _val = entityInfo2.EntityPropertyInfos.Where(i => i.IsColumn && !i.IsIgnoreSelect).Select(i => $"{i.SelectValueFunc(prefix)} {db.AddQuote(_memberName + "." + i.PropNamePure)}").ToStringSeparated(",");
                                        list.Add(_val);
                                        nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                        continue;
                                    }

                                    _val = Visit(exp2);
                                    var memberName = getNestMember();
                                    nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                    var quoteMemberName = db.AddQuote(memberName);
                                    if (_val.EndsWith(quoteMemberName) || _val == memberName || _val.EndsWith("." + memberName))
                                        list.Add(_val);
                                    else
                                        list.Add($"{_val} {quoteMemberName}");
                                }
                                return list.ToStringSeparated(",");
                            }
                        case ExpressionType.New:
                            {
                                var newExp = exp as NewExpression;
                                var args = newExp.Arguments;//Id=i.Id 中的 i.Id
                                var members = newExp.Members;//Id=i.Id 中的 Id=
                                var list = new List<string>();
                                for (int i = 0; i < args.Count; i++)
                                {
                                    var arg = args[i];
                                    string _val;
                                    nestMemberPath.Add(members[i].Name);
                                    if (arg.NodeType == ExpressionType.New || arg.NodeType == ExpressionType.MemberInit)
                                    {
                                        //嵌套 Select(i => new { Ext = new { Ext2 = new { Id = i.Id } } }
                                        _val = Visit(arg);
                                        list.Add(_val);
                                        nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                        continue;
                                    }
                                    if (arg.NodeType == ExpressionType.Parameter && !DBAccess.IsSimple(arg.Type))
                                    {
                                        //Select((i,j)=>new {Person = i}
                                        var entityInfo = db.GetEntityInfoInternal(arg.Type);
                                        var prefix = Visit(arg);
                                        var _memberName = getNestMember();
                                        _val = entityInfo.EntityPropertyInfos.Where(i => i.IsColumn && !i.IsIgnoreSelect).Select(i => $"{i.SelectValueFunc(prefix)} {db.AddQuote(_memberName + "." + i.PropNamePure)}").ToStringSeparated(",");
                                        list.Add(_val);
                                        nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                        continue;
                                    }
                                    //expr中的这个属性可能是常量
                                    _val = Visit(arg);
                                    var memberName = getNestMember();
                                    nestMemberPath.RemoveAt(nestMemberPath.Count - 1);
                                    var quoteMemberName = db.AddQuote(memberName);
                                    if (_val.EndsWith(quoteMemberName) || _val == memberName || _val.EndsWith("." + memberName))
                                        list.Add(_val);
                                    else
                                        list.Add($"{_val} {quoteMemberName}");
                                }
                                return list.ToStringSeparated(",");
                            }
                        case ExpressionType.Add:
                            {
                                var binary = exp as BinaryExpression;
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                if (binary.Left.Type == typeof(string) || binary.Right.Type == typeof(string))
                                {
                                    return db.StringSqlSegment.Add(left, right);
                                }
                                return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                            }
                        case ExpressionType.Subtract:
                        case ExpressionType.Multiply:
                        case ExpressionType.Divide:
                            {
                                var binary = exp as BinaryExpression;
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                            }
                        case ExpressionType.Equal:
                        case ExpressionType.NotEqual:
                            {
                                var binary = exp as BinaryExpression;
                                (isEnumString, enumType) = IsEnumString(binary);
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                if (left == "null")
                                {
                                    return $"{right.WrapBracket()} {(exp.NodeType == ExpressionType.Equal ? "is" : "is not")} null";
                                }
                                else if (right == "null")
                                {
                                    return $"{left.WrapBracket()} {(exp.NodeType == ExpressionType.Equal ? "is" : "is not")} null";
                                }
                                isEnumString = false;
                                return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                            }
                        case ExpressionType.Coalesce:
                            {
                                var binary = exp as BinaryExpression;
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                return $"ifnull({left.WrapBracket()},{right.WrapBracket()})";
                            }
                        case ExpressionType.Conditional:
                            {
                                var condition = exp as ConditionalExpression;
                                var test = Visit(condition.Test);
                                var ifTrue = Visit(condition.IfTrue);
                                var ifFalse = Visit(condition.IfFalse);
                                return $"if({test.WrapBracket()},{ifTrue.WrapBracket()},{ifFalse.WrapBracket()})";
                            }
                        case ExpressionType.GreaterThan:
                        case ExpressionType.GreaterThanOrEqual:
                        case ExpressionType.LessThan:
                        case ExpressionType.LessThanOrEqual:
                        case ExpressionType.AndAlso:
                        case ExpressionType.OrElse:
                            {
                                var binary = exp as BinaryExpression;
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                            }
                        case ExpressionType.Convert:
                        case ExpressionType.TypeAs:
                            {
                                var unary = exp as UnaryExpression;
                                var destType = unary.Type;
                                var fromType = unary.Operand.Type;

                                if ((destType.IsNullable() && destType.GenericTypeArguments[0] == fromType) || (fromType.IsNullable() && fromType.GenericTypeArguments[0] == destType))
                                {
                                    // DateTime? => DateTime 或 DateTime=>DateTime?
                                    // 直接忽略
                                    return Visit(unary.Operand);
                                }
                                if (fromType.IsNullable()) fromType = fromType.GenericTypeArguments[0];
                                if (fromType.IsEnum)
                                {
                                    // 忽略枚举转数字
                                    return Visit(unary.Operand);
                                }
                                return Visit(unary.Operand);
                            }
                        case ExpressionType.Call:
                            {
                                var call = exp as MethodCallExpression;
                                var method = call.Method.GetMethodGenericFullName();
                                var groupFilterCall = (MethodCallExpression call, string funName) =>
                                {
                                    var lambda = call.Arguments[0] as LambdaExpression;
                                    _parameters.Add(lambda.Parameters.FirstOrDefault());
                                    _aliases.Add(new KeyValuePair<ParameterExpression, string>(lambda.Parameters.FirstOrDefault(), aliasesMap.FirstOrDefault().Value));
                                    var seg = Visit(lambda.Body);

                                    return $"{funName}({seg})";
                                };
                                var groupFilterJoinCall = (MethodCallExpression call) =>
                                {
                                    //joinLambda
                                    var joinLambda = call.Arguments[0] as LambdaExpression;
                                    _parameters.AddRange(joinLambda.Parameters);
                                    _aliases.AddRange(joinLambda.Parameters.Select((item, idx) => new KeyValuePair<ParameterExpression, string>(item, aliasesMap.Skip(idx).Take(1).First().Value)));
                                    var join = Visit(joinLambda.Body);

                                    //separatorLambda
                                    var separatorLambda = call.Arguments[1];
                                    var separator = Visit(separatorLambda);

                                    //orderLambda
                                    if (call.Arguments.Count > 2)
                                    {
                                        var orderLambda = call.Arguments[2] as LambdaExpression;
                                        _parameters.AddRange(orderLambda.Parameters);
                                        _aliases.AddRange(orderLambda.Parameters.Select((item, idx) => new KeyValuePair<ParameterExpression, string>(item, aliasesMap.Skip(idx).Take(1).First().Value)));
                                        var order = string.Empty;
                                        if (orderLambda.Body.NodeType == ExpressionType.New)
                                        {
                                            //Join(i=>i.Name,",",i=>new{i.Id,i.Name},false)
                                            var newExp = orderLambda.Body as NewExpression;
                                            var args = newExp.Arguments;
                                            var members = newExp.Members;
                                            var list = new List<string>();
                                            for (int i = 0; i < args.Count; i++)
                                            {
                                                var arg = args[i];
                                                //expr中的这个属性可能是常量
                                                var _val = Visit(arg);
                                                list.Add(_val);
                                            }
                                            order = list.ToStringSeparated(",");
                                        }
                                        else
                                        {
                                            order = Visit(orderLambda.Body);
                                        }
                                        if (call.Arguments.Count > 3)
                                        {
                                            var isDesc = call.Arguments[3];
                                            var isDescVal = GetConstant(isDesc, midValues, "表达式错误,类似 IGroupFilter.Join(i=>i.name, \",\", i=>i.order, desc, isDistinct) desc应为一个确定值!").UnWrap();
                                            var desc = (bool)isDescVal;
                                            if (call.Arguments.Count > 4)
                                            {
                                                var isDistinct = call.Arguments[4];
                                                var isDistinctVal = GetConstant(isDistinct, midValues, "表达式错误,类似 IGroupFilter.Join(i=>i.name, \",\", i=>i.order, desc, isDistinct) isDistinct应为一个确定值!").UnWrap();
                                                var distinct = (bool)isDistinctVal;
                                                return $"group_concat({(distinct ? "distinct " : "")}{join} order by {order} {(desc ? "desc" : "asc")} separator {separator})";
                                            }
                                            return $"group_concat({join} order by {order} {(desc ? "desc" : "asc")} separator {separator})";
                                        }
                                        return $"group_concat({join} order by {order} separator {separator})";
                                    }
                                    return $"group_concat({join} separator {separator})";
                                };

                                var methodHook = db.GetHook(method.Name);
                                if (methodHook != null) return methodHook(new MethodHookContext
                                {
                                    Expression = call,
                                    Reflect = method,
                                    IsEnumString = exp =>
                                    {
                                        var res = IsEnumString(exp);
                                        (isEnumString, enumType) = res;
                                        return res;
                                    },
                                    GetConstant = GetConstant,
                                    MidValues = midValues,
                                    Visit = Visit,
                                    WrapBracket = Extensions.StringExtensions.WrapBracket,
                                    GetJsonSqlSeg = GetJsonSqlSeg,
                                    JsonTableSpread = JsonTableSpread,
                                    db = db,
                                    JsonTableParaToColumns = JsonTableParaToColumns,
                                    AddParameter = (i, b) =>
                                    {
                                        _aliases.Add(i);
                                        _parameters.Add(i.Key);
                                        if (b) _parameterJsons.Add(i.Key);
                                    },
                                    RemoveParameter = i =>
                                    {
                                        _aliases.RemoveFluent(j => j.Key == i);
                                        _parameters.Remove(i);
                                        _parameterJsons.Remove(i);
                                    },
                                }).ToString();
                                switch (method.Name)
                                {
                                    #region IGroupFilter group..group8
                                    #region max
                                    case "TKey DBUtil.Builders.IGroupFilter<T, TGroupKey>.Max<TKey>(System.Func<T, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Max<TKey>(System.Func<T, T2, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, T4, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, T4, T5, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, T4, T5, T6, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Max<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey> expression)":
                                        {
                                            return groupFilterCall(call, "max");
                                        }
                                    #endregion
                                    #region min
                                    case "TKey DBUtil.Builders.IGroupFilter<T, TGroupKey>.Min<TKey>(System.Func<T, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Min<TKey>(System.Func<T, T2, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, T4, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, T4, T5, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, T4, T5, T6, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Min<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey> expression)":
                                        {
                                            return groupFilterCall(call, "min");
                                        }
                                    #endregion
                                    #region sum
                                    case "TKey DBUtil.Builders.IGroupFilter<T, TGroupKey>.Sum<TKey>(System.Func<T, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Sum<TKey>(System.Func<T, T2, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, T4, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, T4, T5, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, T4, T5, T6, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Sum<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey> expression)":
                                        {
                                            return groupFilterCall(call, "sum");
                                        }
                                    #endregion
                                    #region avg
                                    case "TKey DBUtil.Builders.IGroupFilter<T, TGroupKey>.Avg<TKey>(System.Func<T, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Avg<TKey>(System.Func<T, T2, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, T4, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, T4, T5, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, T4, T5, T6, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, TKey> expression)":
                                    case "TKey DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Avg<TKey>(System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey> expression)":
                                        {
                                            return groupFilterCall(call, "avg");
                                        }
                                    #endregion
                                    #region join
                                    //join1 only separator
                                    case "string DBUtil.Builders.IGroupFilter<T, TGroupKey>.Join(System.Func<T, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Join(System.Func<T, T2, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Join(System.Func<T, T2, T3, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Join(System.Func<T, T2, T3, T4, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, string> expression, string separator)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, T8, string> expression, string separator)":
                                    //join2 separator & orderBy & desc
                                    case "string DBUtil.Builders.IGroupFilter<T, TGroupKey>.Join(System.Func<T, string> expression, string separator, System.Func<T, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Join(System.Func<T, T2, string> expression, string separator, System.Func<T, T2, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Join(System.Func<T, T2, T3, string> expression, string separator, System.Func<T, T2, T3, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Join(System.Func<T, T2, T3, T4, string> expression, string separator, System.Func<T, T2, T3, T4, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, string> expression, string separator, System.Func<T, T2, T3, T4, T5, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, T7, System.Object> orderBy, bool desc)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, T8, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, T7, T8, System.Object> orderBy, bool desc)":
                                    //join3 separator & orderBy & desc & isDistinct
                                    case "string DBUtil.Builders.IGroupFilter<T, TGroupKey>.Join(System.Func<T, string> expression, string separator, System.Func<T, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, TGroupKey>.Join(System.Func<T, T2, string> expression, string separator, System.Func<T, T2, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, TGroupKey>.Join(System.Func<T, T2, T3, string> expression, string separator, System.Func<T, T2, T3, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, TGroupKey>.Join(System.Func<T, T2, T3, T4, string> expression, string separator, System.Func<T, T2, T3, T4, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, string> expression, string separator, System.Func<T, T2, T3, T4, T5, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, T7, System.Object> orderBy, bool desc, bool isDistinct)":
                                    case "string DBUtil.Builders.IGroupFilter<T, T2, T3, T4, T5, T6, T7, T8, TGroupKey>.Join(System.Func<T, T2, T3, T4, T5, T6, T7, T8, string> expression, string separator, System.Func<T, T2, T3, T4, T5, T6, T7, T8, System.Object> orderBy, bool desc, bool isDistinct)":
                                        {
                                            return groupFilterJoinCall(call);
                                        }
                                    #endregion
                                    #endregion
                                    #region 字符串方法
                                    case "bool int.Equals(int obj)":
                                        {
                                            var left = Visit(call.Object);
                                            var right = Visit(call.Arguments[0]);
                                            var sql = $"{left} = {right}";
                                            return sql;
                                        }
                                    case "bool string.Equals(string value)":
                                        {
                                            var left = Visit(call.Object);
                                            var right = Visit(call.Arguments[0]);
                                            var sql = $"{left} = {right}";
                                            return sql;
                                        }
                                    case "bool string.StartsWith(string value)":
                                    case "bool string.StartsWith(char value)":
                                        {
                                            var op1 = Visit(call.Object);
                                            var pattern = GetLikeString(call.Arguments[0], EnumLikeMode.Start);
                                            var sql = $"{op1.WrapBracket()} like {pattern}";
                                            return sql;
                                        }
                                    case "bool string.EndsWith(string value)":
                                    case "bool string.EndsWith(char value)":
                                        {
                                            var op1 = Visit(call.Object);
                                            var pattern = GetLikeString(call.Arguments[0], EnumLikeMode.End);
                                            var sql = $"{op1.WrapBracket()} like {pattern}";
                                            return sql;
                                        }
                                    case "bool string.Contains(string value)":
                                    case "bool string.Contains(char value)":
                                        {
                                            var op1 = Visit(call.Object);
                                            var pattern = GetLikeString(call.Arguments[0], EnumLikeMode.Both);
                                            var sql = $"{op1.WrapBracket()} like {pattern}";
                                            return sql;
                                        }
                                    case "string string.ToUpper()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.StringSqlSegment.Upper(op1.WrapBracket());
                                        }
                                    case "string string.ToLower()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.StringSqlSegment.Lower(op1.WrapBracket());
                                        }
                                    case "string string.Trim()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.StringSqlSegment.Trim(op1.WrapBracket());
                                        }
                                    case "string string.TrimStart()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.StringSqlSegment.LeftTrim(op1.WrapBracket());
                                        }
                                    case "string string.TrimEnd()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.StringSqlSegment.RightTrim(op1.WrapBracket());
                                        }
                                    //IndexOf系列
                                    /*
                                    int string.IndexOf(char value)
                                    int string.IndexOf(char value, int startIndex)
                                    int string.IndexOf(char value, System.StringComparison comparisonType)
                                    int string.IndexOf(char value, int startIndex, int count)
                                    int string.IndexOfAny(char[] anyOf)
                                    int string.IndexOfAny(char[] anyOf, int startIndex)
                                    int string.IndexOfAny(char[] anyOf, int startIndex, int count)
                                    int string.IndexOf(string value)
                                    int string.IndexOf(string value, int startIndex)
                                    int string.IndexOf(string value, int startIndex, int count)
                                    int string.IndexOf(string value, System.StringComparison comparisonType)
                                    int string.IndexOf(string value, int startIndex, System.StringComparison comparisonType)
                                    int string.IndexOf(string value, int startIndex, int count, System.StringComparison comparisonType)
                                    int string.LastIndexOf(char value)
                                    int string.LastIndexOf(char value, int startIndex)
                                    int string.LastIndexOf(char value, int startIndex, int count)
                                    int string.LastIndexOfAny(char[] anyOf)
                                    int string.LastIndexOfAny(char[] anyOf, int startIndex)
                                    int string.LastIndexOfAny(char[] anyOf, int startIndex, int count)
                                    int string.LastIndexOf(string value)
                                    int string.LastIndexOf(string value, int startIndex)
                                    int string.LastIndexOf(string value, int startIndex, int count)
                                    int string.LastIndexOf(string value, System.StringComparison comparisonType)
                                    int string.LastIndexOf(string value, int startIndex, System.StringComparison comparisonType)
                                    int string.LastIndexOf(string value, int startIndex, int count, System.StringComparison comparisonType)
                                    */
                                    case "int string.IndexOf(char value)":
                                    case "int string.IndexOf(string value)":
                                        {
                                            var op1 = Visit(call.Object);
                                            var arg = Visit(call.Arguments[0]);
                                            return db.StringSqlSegment.IndexOf(op1.WrapBracket(), arg);
                                        }
                                    case "int string.IndexOf(char value, System.StringComparison comparisonType)":
                                    case "int string.IndexOf(string value, System.StringComparison comparisonType)":
                                        {
                                            var op1 = Visit(call.Object);
                                            var arg = Visit(call.Arguments[0]);
                                            var arg2 = call.Arguments[1];
                                            if (arg2 is not ConstantExpression) throw new Exception($"int string.IndexOf(char value, System.StringComparison comparisonType) 的参数comparisonType只允许使用常量!");
                                            var constArg = arg2 as ConstantExpression;
                                            var cmp = (StringComparison)constArg.Value;
                                            return db.StringSqlSegment.IndexOf(op1.WrapBracket(), arg, cmp.ToString().Contains("IgnoreCase"));
                                        }
                                    case "bool Common.Net.Extensions.StringExtensions.IsNullOrEmpty(string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return db.StringSqlSegment.IsNullOrEmpty(op1);
                                        }
                                    case "bool Common.Net.Extensions.StringExtensions.IsNotNullOrEmpty(string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return $"not {db.StringSqlSegment.IsNullOrEmpty(op1)}";
                                        }
                                    case "bool Common.Net.Extensions.StringExtensions.IsNullOrEmptyOrWhiteSpace(string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return db.StringSqlSegment.IsNullOrEmpty(op1);
                                        }
                                    case "bool Common.Net.Extensions.StringExtensions.IsNotNullOrEmptyOrWhiteSpace(string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return $"not {db.StringSqlSegment.IsNullOrEmpty(op1)}";
                                        }
                                    #endregion
                                    #region Enum ContainsAny Contains
                                    case "bool Common.Net.Extensions.EnumExtensions.ContainsAny<T>(System.Enum enu, T item)":
                                        {
                                            //db.Where<EnumFlags>(type=>type.ContainsAny(EnumFlags.Open|EnumFlags.Close))
                                            //db.WhereSeg<Person>(i => i.FlagNullable.ContainsAny(EnumFlag.Active | EnumFlag.Open))
                                            (isEnumString, enumType) = IsEnumString(call.Arguments[0]);
                                            if (!isEnumString) (isEnumString, enumType) = IsEnumString(call.Arguments[1]);
                                            var op1 = string.Empty;
                                            var op2 = string.Empty;
                                            if (isEnumString)
                                            {
                                                var flag = string.Empty;
                                                var isConstantArg1 = false;
                                                var result = GetConstant(call.Arguments[0], midValues);
                                                if (result.Success)
                                                {
                                                    flag = result.Data.ToString();
                                                    isConstantArg1 = true;
                                                }
                                                else
                                                {
                                                    result = GetConstant(call.Arguments[1], midValues);
                                                    if (result.Success)
                                                    {
                                                        flag = result.Data.ToString();
                                                        isConstantArg1 = false;
                                                    }
                                                }
                                                //最终目标 (concat_ws('',concat_ws('',', ',i.`Flag`),',') like '%, Active,%' or concat_ws('',concat_ws('',', ',i.`Flag`),',') like '%, Close,%')
                                                if (isConstantArg1)
                                                {
                                                    //op1是常量 flag=op1="Open, Close" op2="i.Flag"
                                                    op2 = Visit(call.Arguments[1]);
                                                    op2 = db.StringSqlSegment.Add(db.StringSqlSegment.Add("', '", op2), "','");
                                                    if (flag.Contains(','))
                                                    {

                                                        var arr = flag.SplitAndTrim(',');
                                                        var str = arr.Select(i => $", {i},").Select(i => $"{op2} like '%{i}%'").ToStringSeparated(" or ");
                                                        return $"({str})";
                                                    }
                                                    op1 = $", {flag},";
                                                    return $"{op2} like '%{op1}%'";
                                                }
                                                else
                                                {
                                                    //op2是常量 flag=op1="i.Flag" op2="Open, Close"
                                                    op1 = Visit(call.Arguments[0]);
                                                    op1 = db.StringSqlSegment.Add(db.StringSqlSegment.Add("', '", op1), "','");
                                                    if (flag.Contains(','))
                                                    {
                                                        var arr = flag.SplitAndTrim(',');
                                                        var str = arr.Select(i => $", {i},").Select(i => $"{op1} like '%{i}%'").ToStringSeparated(" or ");
                                                        return $"({str})";
                                                    }
                                                    op2 = $", {flag},";
                                                    return $"{op1} like '%{op2}%'";
                                                }
                                            }
                                            else
                                            {
                                                op1 = Visit(call.Arguments[0]);
                                                op2 = Visit(call.Arguments[1]);
                                                return $"{op1.WrapBracket()} & {op2.WrapBracket()} > 0";
                                            }
                                        }
                                    case "bool Common.Net.Extensions.EnumExtensions.Contains<T>(System.Enum enu, T item)":
                                        {
                                            //db.Where<EnumFlags>(type=>type.Contains(EnumFlags.Open|EnumFlags.Close))
                                            //db.WhereSeg<Person>(i => i.FlagNullable.Contains(EnumFlag.Active | EnumFlag.Open))
                                            (isEnumString, enumType) = IsEnumString(call.Arguments[0]);
                                            if (!isEnumString) (isEnumString, enumType) = IsEnumString(call.Arguments[0]);
                                            var op1 = string.Empty;
                                            var op2 = string.Empty;
                                            if (isEnumString)
                                            {
                                                var flag = string.Empty;
                                                var isConstantArg1 = false;
                                                var result = GetConstant(call.Arguments[0], midValues);
                                                if (result.Success)
                                                {
                                                    flag = result.Data.ToString();
                                                    isConstantArg1 = true;
                                                }
                                                else
                                                {
                                                    result = GetConstant(call.Arguments[1], midValues);
                                                    if (result.Success)
                                                    {
                                                        flag = result.Data.ToString();
                                                        isConstantArg1 = false;
                                                    }
                                                }
                                                //最终目标 concat_ws('',concat_ws('',', ',i.`Flag`),',') like '%, Active,%'
                                                if (isConstantArg1)
                                                {
                                                    //op1是常量 flag=op1="Open, Close" op2="i.Flag"
                                                    op2 = Visit(call.Arguments[1]);
                                                    op2 = db.StringSqlSegment.Add(db.StringSqlSegment.Add("', '", op2), "','");
                                                    if (flag.Contains(','))
                                                    {
                                                        var arr = flag.SplitAndTrim(',');
                                                        var str = arr.Select(i => $", {i},").Select(i => $"{op2} like '%{i}%'").ToStringSeparated(" and ");
                                                        return $"({str})";
                                                    }
                                                    op1 = $", {flag},";
                                                    return $"{op2} like '%{op1}%'";
                                                }
                                                else
                                                {
                                                    //op2是常量 flag=op2="Open, Close" op4="i.Flag"
                                                    op1 = Visit(call.Arguments[0]);
                                                    op1 = db.StringSqlSegment.Add(db.StringSqlSegment.Add("', '", op1), "','");
                                                    if (flag.Contains(','))
                                                    {
                                                        var arr = flag.SplitAndTrim(',');
                                                        var str = arr.Select(i => $", {i},").Select(i => $"{op1} like '%{i}%'").ToStringSeparated(" and ");
                                                        return $"({str})";
                                                    }
                                                    op2 = $", {flag},";
                                                    return $"{op1} like '%{op2}%'";
                                                }
                                            }
                                            else
                                            {
                                                op1 = Visit(call.Arguments[0]);
                                                op2 = Visit(call.Arguments[1]);
                                                return $"{op1.WrapBracket()} & {op2.WrapBracket()} = {op2.WrapBracket()}";
                                            }
                                        }
                                    #endregion
                                    #region ToJsonString ToJson
                                    case "string System.Text.Json.Nodes.JsonNode.ToJsonString(System.Text.Json.JsonSerializerOptions options)":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.ConvertSqlSegment.ConvertAllToString(op1);
                                        }
                                    case "string Common.Net.Extensions.ObjectExtensions.ToJson(System.Object obj, System.Text.Json.JsonSerializerOptions options)":
                                    case "string Common.Net.Extensions.ObjectExtensions.ToJson(System.Object obj)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return db.ConvertSqlSegment.ConvertAllToString(op1);
                                        }
                                    #endregion

                                    #region JsonNode.AsArray/AsObject
                                    case "System.Text.Json.Nodes.JsonArray System.Text.Json.Nodes.JsonNode.AsArray()":
                                        {
                                            return Visit(call.Object);
                                        }
                                    case "System.Text.Json.Nodes.JsonObject System.Text.Json.Nodes.JsonNode.AsObject()":
                                        {
                                            return Visit(call.Object);
                                        }
                                    #endregion

                                    #region 类型转换
                                    case "string int.ToString()":
                                    case "string long.ToString()":
                                    case "string char.ToString()":
                                    case "string short.ToString()":
                                    case "string uint.ToString()":
                                    case "string ushort.ToString()":
                                    case "string ulong.ToString()":
                                    case "string float.ToString()":
                                    case "string double.ToString()":
                                    case "string decimal.ToString()":
                                    case "string byte.ToString()":
                                    case "string sbyte.ToString()":
                                        {
                                            var op1 = Visit(call.Object);
                                            return db.ConvertSqlSegment.ConvertAllToString(op1);
                                        }
                                    case "T[] System.Collections.Generic.List<T>.ToArray()":
                                    case "TSource[] System.Linq.Enumerable.ToArray<TSource>(System.Collections.Generic.IEnumerable<TSource> source)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            return op1;
                                        }
                                    case "T Common.Net.Extensions.ObjectExtensions.To<T>(System.Object value, System.Object[] parameters)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var reflect = call.Method.GetMethodGenericFullName();
                                            var name = reflect.GenericTypes[0].type.GetClassFullName();
                                            if (sourceArrayNumber.Contains(name))
                                            {
                                                return db.ConvertSqlSegment.ConvertStringToInteger(op1);
                                            }
                                            else if (sourceArrayDecimal.Contains(name))
                                            {
                                                return db.ConvertSqlSegment.ConvertStringToDecimal(op1);
                                            }
                                            return db.ConvertSqlSegment.ConvertAllToString(op1);
                                        }
                                    #endregion

                                    #region subSelect
                                    #region SelectBase
                                    case "long DBUtil.Builders.SelectBuilderBase.Count()":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.Count, null, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region select..select8
                                    #region FirstOrDefault
                                    case "Dto DBUtil.Builders.SelectBuilder<T>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, Dto>> expression)":
                                    case "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, Dto>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlFirstOrDefaultInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [(call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region ToList
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, Dto>> expression)":
                                    case "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, Dto>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.ToList, (call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region Max
                                    case "TKey DBUtil.Builders.SelectBuilder<T>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.Max, (call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region Min
                                    case "TKey DBUtil.Builders.SelectBuilder<T>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.Min, (call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region Sum
                                    case "TKey DBUtil.Builders.SelectBuilder<T>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.Sum, (call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #region Avg
                                    case "TKey DBUtil.Builders.SelectBuilder<T>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)":
                                    case "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)":
                                        {
                                            //todo update/insert/select 中嵌套subselect
                                            var selectBuilder = ExeSelectBuilder(call.Object, midValues);
                                            var methodInfo = selectBuilder.GetType().GetMethod("ToSqlInternal", BindingFlags.Instance | BindingFlags.NonPublic);
                                            var obj = methodInfo.Compile()(selectBuilder, [EnumSelectToSql.Avg, (call.Arguments[0] as UnaryExpression).Operand, midValues, aliasesMap]);
                                            return "(" + obj.ToString().TrimEnd(';') + ")";
                                        }
                                    #endregion
                                    #endregion
                                    #endregion

                                    #region ifnulluse todo:IfNullUseEmpty
                                    case "T Common.Net.Extensions.IfNullUseExtensions.IfNullUse<T>(T obj, T value)":
                                    case "T Common.Net.Extensions.IfNullUseExtensions.IfNullUse<T>(T? obj, T value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var op2 = Visit(call.Arguments[1]);
                                            return $"ifnull({op1},{op2})";
                                        }
                                    case "T Common.Net.Extensions.IfNullUseExtensions.IfNullUseNew<T>(T obj)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var jsonDataType = db.ParseJsonDataType(call.Arguments[0].Type);
                                            var op2 = jsonDataType switch
                                            {
                                                EnumJsonDataType.Object => "json_object()",
                                                EnumJsonDataType.String => "\"\"",
                                                EnumJsonDataType.Number => "0",
                                                EnumJsonDataType.Null => "null",
                                                EnumJsonDataType.Bool => "false",
                                                EnumJsonDataType.Array => "json_array()",
                                                _ => throw new NotImplementedException(),
                                            };
                                            return $"ifnull({op1},{op2})";
                                        }
                                    case "T Common.Net.Extensions.IfNullUseExtensions.IfDefaultUse<T>(T obj, T value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var op2 = Visit(call.Arguments[1]);
                                            var def = call.Arguments[0].Type.GetDefault();
                                            if (def == null)
                                            {
                                                return $"ifnull({op1},{op2})";
                                            }
                                            else
                                            {
                                                return $"if({op1} = {db.ConvertToSqlSeg(def)},{op2})";
                                            }
                                        }
                                    case "T Common.Net.Extensions.IfNullUseExtensions.IfNullOrInitValueUse<T>(T obj, T value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var op2 = Visit(call.Arguments[1]);
                                            var def = call.Arguments[0].Type.GetDefault();
                                            if (def == null)
                                            {
                                                return $"if({op1} is null,{op2})";
                                            }
                                            else
                                            {
                                                return $"if(({op1} is null) or ({op1} = {db.ConvertToSqlSeg(def)}),{op2})";
                                            }
                                        }
                                    case "string Common.Net.Extensions.IfNullUseExtensions.IsNullOrWhiteSpaceUse(string str, string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var op2 = Visit(call.Arguments[1]);
                                            return $"if({db.StringSqlSegment.IsNullOrWhiteSpace(op1)},{op2},{op1})";
                                        }
                                    case "string Common.Net.Extensions.IfNullUseExtensions.IsNullOrEmptyUse(string str, string value)":
                                        {
                                            var op1 = Visit(call.Arguments[0]);
                                            var op2 = Visit(call.Arguments[1]);
                                            return $"if({db.StringSqlSegment.IsNullOrEmpty(op1)},{op2},{op1})";
                                        }
                                    #endregion

                                    default:
                                        {
                                            throw new Exception($"不支持解析此lambda表达式:{method.Name}");
                                        }
                                }
                            }
                        case ExpressionType.And:
                        case ExpressionType.Or:
                            {
                                var binary = exp as BinaryExpression;
                                var left = Visit(binary.Left);
                                var right = Visit(binary.Right);
                                if (binary.Left.Type == typeof(bool)) return $"{left.WrapBracket()} {_cache[exp.NodeType]} {right.WrapBracket()}";
                                else return $"{left.WrapBracket()} {(exp.NodeType == ExpressionType.And ? "&" : "|")} {right.WrapBracket()}";
                            }
                        case ExpressionType.ArrayLength:
                            {
                                var unary = exp as UnaryExpression;
                                var op1 = Visit(unary.Operand);
                                var type = unary.Operand.Type;
                                var hook = db.GetHook("other:getArrayLength");
                                if (hook != null) return hook(new object[] { op1, type }) as string;
                                else throw new Exception("缺少hook:other:getArrayLength!");
                            }
                        case ExpressionType.ArrayIndex:
                            {
                                var binary = exp as BinaryExpression;
                                var op1 = Visit(binary.Left);
                                var _ret = GetConstant(binary.Right, midValues);
                                var _val = string.Empty;
                                if (_ret.Success)
                                {
                                    _val = _ret.Data.ToString();
                                }
                                else
                                {
                                    _val = Visit(binary.Right);
                                }
                                var hook = db.GetHook("other:getArrayIndex");
                                if (hook != null) return hook(new object[] { op1, _val, _ret.Success, binary.Type }) as string;
                                else throw new Exception("缺少hook:other:getArrayIndex!");
                            }
                        case ExpressionType.Lambda:
                            {
                                var lambda = exp as LambdaExpression;
                                return Visit(lambda.Body);
                            }
                        case ExpressionType.Not:
                            {
                                var unary = exp as UnaryExpression;
                                var op1 = Visit(unary.Operand);
                                return $"not ({op1})";
                            }
                        default:
                            break;
                    }
                    throw new Exception($"不能处理的表达式类型: {exp.NodeType}, {exp}");
                }
            }
            string GetLikeString(Expression expression, EnumLikeMode mode)
            {
                var ret = GetConstant(expression, midValues, null);
                var pattern = string.Empty;
                if (ret.Success)
                {
                    pattern = db.ConvertToSqlSeg(ret.Data).Data.ToString();
                    pattern = pattern.Substring(1, pattern.Length - 2);
                    pattern = pattern.Replace("%", "\\%").Replace("_", "\\_");
                    if (mode == EnumLikeMode.Start) pattern = $"'{pattern}%'";
                    else if (mode == EnumLikeMode.End) pattern = $"'%{pattern}'";
                    else pattern = $"'%{pattern}%'";
                }
                else
                {
                    pattern = Visit(expression);
                    pattern = db.StringSqlSegment.ReplaceAll(db.StringSqlSegment.ReplaceAll(pattern, "'%'", "'\\%'"), "'_'", "'\\_'");
                    if (mode == EnumLikeMode.Start) pattern = db.StringSqlSegment.Add(pattern, "'%'");
                    else if (mode == EnumLikeMode.End) pattern = db.StringSqlSegment.Add("'%'", pattern);
                    else pattern = db.StringSqlSegment.Add("'%'", db.StringSqlSegment.Add(pattern, "'%'"));
                }
                return pattern;
            }
            string[] GetJsonSqlSeg(Expression[] expressions, EnumJsonAcceptAsType[] hookParas = null)
            {
                var arr = new string[expressions.Length];
                for (int i = 0; i < arr.Length; i++)
                {
                    var res = GetConstant(expressions[i], midValues);
                    var regardType = (hookParas.IsNotNullOrEmpty() && hookParas.Length > i) ? hookParas[i] : default;
                    arr[i] = res.Success ? db.ConvertJsonLiteralToSql(res.Data, regardType).res : Visit(expressions[i]);
                }
                return arr;
            }
            (bool isEnumString, Type enumType) IsEnumString(Expression exp)
            {
                //处理 enumstring
                (bool isEnumString, Type enumType) res = (false, null);
                if (exp is BinaryExpression binary)
                {
                    res = IsEnumString(binary.Left);
                    if (!res.isEnumString) res = IsEnumString(binary.Right);
                }
                else if (exp is MemberExpression memberExp)
                {
                    if (memberExp.Expression.Type.IsNullable()) res = IsEnumString(memberExp.Expression);
                    else if (!memberExp.Expression.Type.IsInterface
                        && !memberExp.Expression.Type.IsValueType
                        && memberExp.Expression.Type != typeof(string))
                    {
                        var entity = db.GetEntityInfoInternal(memberExp.Expression.Type);
                        var member = entity.EntityPropertyInfos.FirstOrDefault(i => i.PropNamePure == memberExp.Member.Name);
                        if (member != null && member.IsEnum && member.IsDbString == true)
                        {
                            res = (true, member.Type);
                        }
                    }
                }
                else if (exp is ParameterExpression parameterExpression)
                {
                    if (enum2StringMap?.TryGetValue(parameterExpression, out bool _b) == true)
                    {
                        var type = parameterExpression.Type.IsNullable() ? Nullable.GetUnderlyingType(parameterExpression.Type) : parameterExpression.Type;
                        if (type.IsEnum)
                        {
                            res = (_b, type);
                        }
                    }
                }
                else if (exp is UnaryExpression unaryExp)
                {
                    if (unaryExp.NodeType == ExpressionType.Convert) res = IsEnumString(unaryExp.Operand);
                }
                return res;
            }
            (List<(string memberName, string colPath, string colType)>, string) JsonTableSpread(LambdaExpression lambda)
            {
                var para = lambda.Parameters.First();

                //如: i.Persons.Any(i=>i.Age>18) => ("i.Age","Age","int")
                //如: i.Scores.Any(i=>i>60) => ("i","","int")
                var jsonTablePara = new List<(string memberName, string colPath, string colType)>();
                nodeCallBacks ??= [];
                var links = new List<(string memberName, string colType)>();
                var linkActive = false;
                var combine = () =>
                {
                    var sb = new StringBuilder();
                    var sb2 = new StringBuilder();
                    var colPath = links.Skip(1).Select(i => i.memberName).ToStringSeparated(".");
                    var memberName = links.Select(i => i.memberName).ToStringSeparated(".");
                    return (memberName, colPath);
                };
                var nodeCallBack = new NodeCallBack(exp => true, (exp, next, visit) =>
                {
                    if (exp is ParameterExpression parameter)
                    {
                        if (parameter == para)
                        {
                            //命中
                            if (linkActive)
                            {
                                //旧的收尾
                                var (memberName, colPath) = combine();
                                jsonTablePara.Add((memberName, colPath, links.Last().colType));
                                linkActive = false;
                                links.Clear();
                            }
                            linkActive = true;
                            var colType = db.Setting.GetDefaultDbtype(db, parameter.Type);
                            var name = parameter.Name;
                            if (_aliases.IsNotNullOrEmpty())
                            {
                                var tmp = _aliases.FirstOrDefault(i => i.Key == parameter);
                                //使用预设的别名
                                if (tmp.Key != null) name = tmp.Value;
                            }
                            links.Add((name, colType));
                            return $"t.{db.AddQuote(name)}";
                        }
                        else
                        {
                            //清空
                            linkActive = false;
                            links.Clear();
                        }
                        return next();
                    }
                    var ret = next();
                    if (linkActive)
                    {
                        //合并成json col                        
                        if (exp is MemberExpression memberExp)
                        {
                            var colType = db.Setting.GetDefaultDbtype(db, memberExp.Type);
                            links.Add((memberExp.Member.Name, colType));
                            var (memberName, colPath) = combine();
                            return $"t.{db.AddQuote(memberName)}";
                        }
                        else
                        {
                            //其他的
                            var (memberName, colPath) = combine();
                            jsonTablePara.Add((memberName, colPath, links.Last().colType));
                            linkActive = false;
                            links.Clear();
                            return ret;
                        }
                    }
                    return ret;
                }
                , 9000);
                //先备份原来的 nodeCallBack
                var bakNodeCallBacks = new List<NodeCallBack>();
                for (int i = 0; i < nodeCallBacks.Count; i++)
                {
                    if (nodeCallBacks[i].Flag is 9000)
                    {
                        bakNodeCallBacks.Add(bakNodeCallBacks);
                        nodeCallBacks.RemoveAt(i);
                        i--;
                        continue;
                    }
                    break;
                }
                //插入新的回调
                nodeCallBacks.Insert(0, nodeCallBack);
                var op2 = Visit(lambda);
                //移除新的回调
                nodeCallBacks.Remove(nodeCallBack);
                //还原原来的 nodeCallBack
                for (int i = bakNodeCallBacks.Count - 1; i >= 0; i--)
                {
                    nodeCallBacks.Insert(0, bakNodeCallBacks[i]);
                }
                bakNodeCallBacks.Clear();
                return (jsonTablePara, op2);
            }
            string JsonTableParaToColumns(List<(string memberName, string colPath, string colType)> jsonTablePara)
            {
                return jsonTablePara.Select(i =>
                {
                    if (i.colPath.IsNotNullOrEmptyOrWhiteSpace()) return $"{db.AddQuote(i.memberName)} {i.colType} path '$.{i.colPath}'";
                    return $"{db.AddQuote(i.memberName)} {i.colType} path '$'";
                }).Distinct().ToStringSeparated(",");
            }
        }

        /// <summary>
        /// 执行一段 expression 返回一个 SelectBuilder
        /// </summary>
        internal static object ExeSelectBuilder(Expression expression, Dictionary<ParameterExpression, object> midValues)
        {
            //常量: subselect i=>db.Select<Person>().Where(j=>j.Id==1)
            var res = GetConstant(expression, midValues);
            if (res.Success) return res.Data;

            //非常量: subselect i=>db.Select<Person>().Where(j=>j.Id==i.Id)
            //肯定是方法
            var exp = expression;
            var call = exp as MethodCallExpression;
            var res2 = GetConstant(call.Object, midValues);
            object obj;
            if (res2.Success)
            {
                //只需要执行一次就可以了
                obj = res2.Data;
            }
            else
            {
                //需要执行多次, 如: sub select i=>db.Select<Person>().Where(j=>j.Id==i.Id).Where(j=>j.Age==i.Age)
                obj = ExeSelectBuilder(call.Object, midValues);
            }
            //执行方法
            Console.WriteLine($"this a a ");
            var args = new object[call.Arguments.Count];
            for (int i = 0; i < call.Arguments.Count; i++)
            {
                if (call.Arguments[i].NodeType == ExpressionType.Quote)
                {
                    //subselect: i=>db.Select<Person>().Where(j=>j.Id==i.Id)
                    args[i] = (call.Arguments[i] as UnaryExpression).Operand;
                }
                else
                {
                    //subselect: i=>db.Select<Person>().WhereIf(localNums.Count>0,j=>j.Id==i.Id)
                    var res3 = GetConstant(call.Arguments[i], midValues);
                    if (res3.Success)
                    {
                        args[i] = res3.Data;
                    }
                    throw new Exception($"在子查询中只允许出现类似 Where(lambda) 或 WhereIf(constant,lambda) 形式的方法调用!");
                }
            }
            var func = call.Method.Compile();
            return func(obj, args);
        }

        /// <summary>
        /// 如果 expression 是 ConstantExpression 返回 Result.Ok constant.Value<br/>
        /// 如果 expression 是 ParameterExpression 并且 midValues 包含 parameter 返回 Result.Ok midValues[parameter]<br />
        /// 找不到常量值 返回 Result.NotOk(errMsg)
        /// </summary>
        internal static Result GetConstant(Expression expression, Dictionary<ParameterExpression, object> midValues, string errMsg = null)
        {
            if (expression is ConstantExpression constant) return Result.Ok().SetData(constant.Value);
            else if (expression is ParameterExpression parameter && midValues.IsNotNullOrEmpty() && midValues.TryGetValue(parameter, out object _val))
                return Result.Ok().SetData(_val);
            return Result.NotOk(errMsg);
        }

        /// <summary>
        /// 用于嵌入到 ParseSql() 中的参数回调, 因为在解析lambda的时候, 读和写需要不同的处理
        /// </summary>
        internal static Func<Expression, Func<string>, Func<Expression, string>, string> WriteCallBack(DBAccess db, Dictionary<ParameterExpression, object> midValues)
        {
            string buildObj(List<(string key, Expression val)> kvs, Func<Expression, string> visit)
            {
                var sb = new StringBuilder();
                sb.Append($"json_object(");
                for (int i = 0; i < kvs.Count; i++)
                {
                    var item = kvs[i];
                    var exp = item.val;
                    if (i > 0) sb.Append(',');
                    var dataType = db.ParseJsonDataType(exp);
                    var val = db.ConvertJsonVariableToSql(visit(exp), dataType, EnumJsonAcceptAsType.Value);
                    sb.Append($"'{db.EscapeString(item.key)}',{val}");
                }
                sb.Append(')');
                return sb.ToString();
            }

            string buildArr(List<Expression> vals, Func<Expression, string> visit)
            {
                var sb = new StringBuilder();
                sb.Append($"json_array(");
                for (int i = 0; i < vals.Count; i++)
                {
                    var exp = vals[i];
                    if (i > 0) sb.Append(',');
                    sb.Append(visit(exp));
                }
                sb.Append(')');
                return sb.ToString();
            }

            return (Expression initExp, Func<string> next, Func<Expression, string> visit) =>
            {
                var nodeType = initExp.NodeType;
                switch (nodeType)
                {
                    case ExpressionType.MemberInit:
                        {
                            var memberInitExpression = initExp as MemberInitExpression;
                            var bindings = memberInitExpression.Bindings;
                            var list = bindings.Select(i => (i.Member.Name, (i as MemberAssignment).Expression)).ToList();
                            return buildObj(list, visit);
                        }
                    case ExpressionType.ListInit:
                        {
                            var listInitExpression = initExp as ListInitExpression;
                            var reflect = initExp.Type.GetClassGenericFullName();
                            var isDic = reflect.Name == "System.Collections.Generic.Dictionary<TKey, TValue>";
                            if (isDic)
                            {
                                //把字典的初始化看成对象

                                var list = listInitExpression.Initializers.Select(i => (GetConstant(i.Arguments[0], midValues).Data.ToString(), i.Arguments[1])).ToList();
                                return buildObj(list, visit);
                            }
                            else
                            {
                                return buildArr(listInitExpression.Initializers.Select(i => i.Arguments[0]).ToList(), visit);
                            }
                        }
                    case ExpressionType.NewArrayInit:
                        {
                            var arrInitExpression = initExp as NewArrayExpression;
                            return buildArr(arrInitExpression.Expressions.ToList(), visit);
                        }
                    case ExpressionType.NewArrayBounds:
                        {
                            return "json_array()";
                        }
                    case ExpressionType.New:
                        {
                            //匿名类 new {}
                            var newExpression = initExp as NewExpression;
                            var arguments = newExpression.Arguments;
                            if (initExp.Type.IsAnonymous())
                            {
                                //new{}
                                var list = new List<(string, Expression)>();
                                var index = 0;
                                while (index < arguments.Count)
                                {
                                    list.Add((newExpression.Members[index].Name, arguments[index]));
                                    index++;
                                }
                                return buildObj(list, visit);
                            }
                            else
                            {
                                var reflect = initExp.Type.GetClassGenericFullName();
                                if (reflect.Name == "System.Text.Json.Nodes.JsonObject")
                                {
                                    if (newExpression.Constructor.ToString() == "Void .ctor(System.Nullable`1[System.Text.Json.Nodes.JsonNodeOptions])")
                                    {
                                        return "json_object()";
                                    }
                                    else if (newExpression.Constructor.ToString() == "Void .ctor(System.Collections.Generic.IEnumerable`1[System.Collections.Generic.KeyValuePair`2[System.String,System.Text.Json.Nodes.JsonNode]], System.Nullable`1[System.Text.Json.Nodes.JsonNodeOptions])")
                                    {
                                        var newKeyPair = newExpression.Arguments[0];
                                        var res = GetConstant(newKeyPair, midValues);
                                        if (res.Success)
                                        {
                                            var tmp = (KeyValuePair<string, JsonNode>[])res.Data;
                                            var dic = new Dictionary<string, object>();
                                            foreach (var item in tmp)
                                                dic.Add(item.Key, item.Value);
                                            return db.ConvertJsonLiteralToSql(dic, EnumJsonAcceptAsType.Value).res;
                                        }
                                        else
                                        {
                                            var newArrExp = newKeyPair as NewArrayExpression;
                                            var tmp = newArrExp.Expressions.Select(i => (KeyValuePair<string, JsonNode>)GetConstant(i, midValues).Data);
                                            var dic = new Dictionary<string, object>();
                                            foreach (var item in tmp)
                                                dic.Add(item.Key, item.Value);
                                            return db.ConvertJsonLiteralToSql(dic, EnumJsonAcceptAsType.Value).res;
                                        }
                                    }
                                    //todo other
                                }
                                else if (reflect.Name == "System.Text.Json.Nodes.JsonArray")
                                {
                                    if (newExpression.Constructor.ToString() == "Void .ctor(System.Text.Json.Nodes.JsonNode[])")
                                    {
                                        var arrInit = newExpression.Arguments[0];
                                        return visit(arrInit);
                                    }
                                    else if (newExpression.Constructor.ToString() == "Void .ctor(System.Text.Json.Nodes.JsonNodeOptions, System.Text.Json.Nodes.JsonNode[])")
                                    {
                                        var arrInit = newExpression.Arguments[1];
                                        return visit(arrInit);
                                    }
                                    else
                                    {
                                        return "json_array()";
                                    }
                                }
                                return "";
                            }
                        }
                    case ExpressionType.Parameter:
                        {
                            var parameter = initExp as ParameterExpression;
                            if (midValues.TryGetValue(parameter, out var value))
                                return db.ConvertJsonLiteralToSql(value, EnumJsonAcceptAsType.Value).res;
                            else
                                return next();
                        }
                    default: throw new Exception($"WriteCallBack无法处理 {nodeType} 类型表达式!");
                }
            };
        }

        internal static bool WriteCallBackCondition(Expression exp)
        {
            var nodeType = exp.NodeType;
            switch (nodeType)
            {
                case ExpressionType.MemberInit:
                case ExpressionType.ListInit:
                case ExpressionType.NewArrayInit:
                case ExpressionType.NewArrayBounds:
                case ExpressionType.New:
                case ExpressionType.Parameter:
                    return true;
                default:
                    return false;
            }
        }

        #region _selectMethods
        private static List<string> _selectMethods = [
           //selectbase
           "long DBUtil.Builders.SelectBuilderBase.Count()",
            //select1
            "Dto DBUtil.Builders.SelectBuilder<T>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, TKey>> expression)",
            
            //auto-generated selectT2
            "Dto DBUtil.Builders.SelectBuilder<T, T2>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, TKey>> expression)",
            //auto-generated selectT3
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, TKey>> expression)",
            //auto-generated selectT4
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, TKey>> expression)",
            //auto-generated selectT5
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, TKey>> expression)",
            //auto-generated selectT6
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, TKey>> expression)",
            //auto-generated selectT7
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, TKey>> expression)",
            //auto-generated selectT8
            "Dto DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.FirstOrDefault<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, Dto>> expression)",
            "System.Collections.Generic.List<Dto> DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.ToList<Dto>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, Dto>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Max<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Min<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Sum<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)",
            "TKey DBUtil.Builders.SelectBuilder<T, T2, T3, T4, T5, T6, T7, T8>.Avg<TKey>(System.Linq.Expressions.Expression<System.Func<T, T2, T3, T4, T5, T6, T7, T8, TKey>> expression)",
        ];
        private static readonly string[] sourceArrayNumber = [
                                            "int", "long", "short", "unit", "ulong", "ushort","byte","sbyte",
                                            "int?", "long?", "short?", "unit?", "ulong?", "ushort?","byte?","sbyte?"
                                        ];
        private static readonly string[] sourceArrayDecimal = ["decimal", "float", "double", "decimal?", "float?", "double?"];
        #endregion
        private static bool FromSelect(Expression exp)
        {
            //from select
            if (exp.NodeType == ExpressionType.Call)
            {
                var callExp = exp as MethodCallExpression;
                var methodName = callExp.Method.GetMethodGenericFullName();
                if (_selectMethods.Contains(methodName.Name)) return true;
            }
            return false;
        }
        /// <summary>
        /// 判断表达式是否可以被简化,仅 new CustomeClass{...} 和 new {...} 不可被简化，其他的都能被求值简化，如：
        /// <list type="bullet">
        /// <item>new CustomeClass()</item>
        /// <item>new Dic{}</item>
        /// <item>new List{}</item>
        /// <item>new Arr{}</item>
        /// <item>new JsonObject()</item>
        /// <item>new KeyValuePair()</item>
        /// </list>
        /// </summary>
        /// <remarks>如果 <c>new {...}</c> 也被简化的话,那么局部更新将变成全量更新</remarks>
        internal static bool IsKeepCallBackForUpdate(Expression exp)
        {
            //只有 new DemoClass {Name = ...} 或 new {...} 这种形式才能阻止被简化,其他的都不能
            //因为只有它才能看做是局部更新,其他的 new DemoClass() new Arr new Dic new List new JsonObject等都看做是全覆盖更新
            if (exp.NodeType == ExpressionType.MemberInit) return true;
            //如果 new {...}被简化,则变成了全量更新(json中)
            if (exp.NodeType == ExpressionType.New && exp.Type.IsAnonymous()) return true;
            //from select
            if (FromSelect(exp)) return true;
            return false;
        }

        /// <summary>
        /// 判断表达式是否可以被简化,仅 subselect 不能被简化，其他的都能被求值简化
        /// </summary>
        internal static bool IsKeepCallBackForWhere(Expression exp) => FromSelect(exp);

        #region 非json格式 得到一个sqlseg
        /// <summary>
        /// setVal 来源: <br />
        /// - 常量<br/>
        /// - lambda set(i=>i.col,i=>{})<br/>
        /// - assignExp SetExpr(i=>new {...})<br/>
        /// </summary>
        internal static string GetSqlSegFromVal(DBAccess db, object setVal, EntityInfo entityInfo, IEnumerable<ParameterExpression> parameterExpressions = null, Dictionary<ParameterExpression, object> midValues = null, bool enum2String = false)
        {
            //下面的逻辑 只有最终常量时才考虑 enum2String
            if (setVal == null) return "null";
            string val = null;
            if (setVal is Expression _exp)
            {
                //表达式
                if (setVal is LambdaExpression lambdaExpression)
                {
                    var ret = ExpressionHelper.ReduceLambda(_exp as LambdaExpression, initParameters: midValues?.Select(i => i.Key).ToList());
                    if (midValues.IsNotNullOrEmpty()) midValues.ForEach(i => ret.midValues.Add(i.Key, i.Value));
                    var res = GetConstant(ret.exp, ret.midValues);
                    if (res.Success)
                    {
                        val = db.ConvertToSqlSeg(res.Data, enum2String).UnWrap();
                    }
                    else
                    {
                        //处理的是非json列,不可能有 new ... 这种格式的
                        val = BuilderHelper.ParseSql(ret.exp, db,
                           aliasesMap: lambdaExpression.Parameters.Select(i => new KeyValuePair<ParameterExpression, string>(i, null)),
                           parameters: lambdaExpression.Parameters.Select(i => i),
                           midValues: ret.midValues);
                    }
                }
                else
                {
                    //肯定已经简化过,不用再简化
                    //处理的是非json列,不可能有 new ... 这种格式的
                    val = BuilderHelper.ParseSql(_exp, db,
                        aliasesMap: parameterExpressions?.Select(i => new KeyValuePair<ParameterExpression, string>(i, null)),
                        parameters: parameterExpressions,
                        midValues: midValues);
                }
            }
            else
            {
                val = db.ConvertToSqlSeg(setVal, enum2String).UnWrap();
            }
            return val;
        }
        #endregion

        #region ReplaceIMultiTable
        /// <summary>
        /// 将 IMultiTable&lt;T,T2>=> 替换为: (T,T2)=>
        /// </summary>
        public static LambdaExpression ReplaceIMultiTable(LambdaExpression expression, Type[] types)
        {
            var paras = types.Select(i => Expression.Parameter(i)).ToList();
            var _body = visit(expression.Body);
            var newExp = Expression.Lambda(_body, paras);
            return newExp;

            Expression visit(Expression expr)
            {
                if (expr == null) return null;
                if (expr is BinaryExpression binary)
                {
                    var left = visit(binary.Left);
                    var right = visit(binary.Right);
                    return binary.Update(left, binary.Conversion, right);
                }
                else if (expr is UnaryExpression unary)
                {
                    var operand = visit(unary.Operand);
                    return unary.Update(operand);
                }
                else if (expr is MemberExpression member)
                {
                    if (member.Expression == expression.Parameters[0])
                    {
                        if (member.Member.Name == "t") return paras[0];
                        if (member.Member.Name == "t2") return paras[1];
                        if (member.Member.Name == "t3") return paras[2];
                        if (member.Member.Name == "t4") return paras[3];
                        if (member.Member.Name == "t5") return paras[4];
                        if (member.Member.Name == "t6") return paras[5];
                        if (member.Member.Name == "t7") return paras[6];
                        if (member.Member.Name == "t8") return paras[7];
                        else throw new Exception($"最多支持到 IMultiTable<...>.t8");
                    }
                    var ex = visit(member.Expression);
                    return member.Update(ex);
                }
                else if (expr is MethodCallExpression call)
                {
                    var obj = visit(call.Object);
                    var args = call.Arguments.Select(i => visit(i)).ToList();
                    return call.Update(obj, args);
                }
                else if (expr is ConditionalExpression conditional)
                {
                    var test = visit(conditional.Test);
                    var ifTrue = visit(conditional.IfTrue);
                    var ifFalse = visit(conditional.IfFalse);
                    return conditional.Update(test, ifTrue, ifFalse);
                }
                else if (expr is ConstantExpression constant)
                {
                    return constant;
                }
                else if (expr is InvocationExpression invocation)
                {
                    var obj = visit(invocation.Expression);
                    var args = invocation.Arguments.Select(i => visit(i)).ToList();
                    return invocation.Update(obj, args);
                }
                else if (expr is LambdaExpression lambda)
                {
                    var body = visit(lambda.Body);
                    return Expression.Lambda(body, lambda.Parameters);
                }
                else if (expr is ListInitExpression listInit)
                {
                    var newExpr = visit(listInit.NewExpression) as NewExpression;
                    var inits = listInit.Initializers.Select(i => i.Update(i.Arguments.Select(i => visit(i)))).ToList();
                    return listInit.Update(newExpr, inits);
                }
                else if (expr is MemberInitExpression memberInit)
                {
                    var newExpr = visit(memberInit.NewExpression) as NewExpression;
                    var inits = memberInit.Bindings.Select(i => visitMemberBinding(i)).ToList();
                    return memberInit.Update(newExpr, inits);
                }
                else if (expr is NewExpression newExpression)
                {
                    return newExpression.Update(newExpression.Arguments.Select(i => visit(i)).ToList());
                }
                else if (expr is NewArrayExpression newArrExpression)
                {
                    return newArrExpression.Update(newArrExpression.Expressions.Select(i => visit(i)).ToList());
                }
                else if (expr is ParameterExpression parameter)
                {
                    return parameter;
                }
                else if (expr is TypeBinaryExpression typeIs)
                {
                    return typeIs.Update(visit(typeIs.Expression));
                }
                else if (expr is DefaultExpression defaultExpression)
                {
                    return expr;
                }
                else if (expr is IndexExpression indexExpt)
                {
                    return indexExpt.Update(visit(indexExpt.Object), indexExpt.Arguments.Select(i => visit(i)));
                }
                return expr;

                MemberBinding visitMemberBinding(MemberBinding memberBinding)
                {
                    if (memberBinding.BindingType == MemberBindingType.Assignment)
                    {
                        var memberAssign = memberBinding as MemberAssignment;
                        return memberAssign.Update(visit(memberAssign.Expression));
                    };
                    if (memberBinding.BindingType == MemberBindingType.ListBinding)
                    {
                        var memberList = memberBinding as MemberListBinding;
                        return memberList.Update(memberList.Initializers.Select(i => i.Update(i.Arguments.Select(i => visit(i)))));
                    };
                    if (memberBinding.BindingType == MemberBindingType.MemberBinding)
                    {
                        var memberMember = memberBinding as MemberMemberBinding;
                        return memberMember.Update(memberMember.Bindings.Select(i => visitMemberBinding(i)));
                    };
                    return null;
                }
            };
        }
        #endregion
    }
}